#include "llvm/Support/CommandLine.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "llvm/Support/Host.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Frontend/ASTConsumers.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/Parse/ParseAST.h"
#include "clang/AST/ASTContext.h"

#include <iostream>

static llvm::cl::opt<std::string> FileName(llvm::cl::Positional, llvm::cl::desc("Input file"), llvm::cl::Required);

int file_size(char* filename)
{
	FILE *fp=fopen(filename,"r");

	if(!fp) {
		return -1;
	}

	fseek(fp, 0, SEEK_END);
	int size=ftell(fp);
	fclose(fp);

	return size;
}

int main(int argc, char **argv)
{
	llvm::cl::ParseCommandLineOptions(argc, argv, "preprocess test\n");

	clang::CompilerInstance ci;
	ci.createDiagnostics();

	std::shared_ptr<clang::TargetOptions> pto = std::make_shared<clang::TargetOptions>();
	pto->Triple = llvm::sys::getDefaultTargetTriple();
	clang::TargetInfo *pti= clang::TargetInfo::CreateTargetInfo(ci.getDiagnostics(), pto);
	ci.setTarget(pti);

	ci.createFileManager();
	ci.createSourceManager(ci.getFileManager());
	ci.createPreprocessor(clang::TU_Complete);

	const clang::FileEntry *file = ci.getFileManager().getVirtualFile(FileName, file_size(argv[1]), 0);
	clang::FileID mainFileID = ci.getSourceManager().createFileID(file, clang::SourceLocation(), clang::SrcMgr::C_User);
	ci.getSourceManager().setMainFileID(mainFileID);

	ci.getPreprocessor().EnterMainSourceFile();
	ci.getDiagnosticClient().BeginSourceFile(ci.getLangOpts(), &ci.getPreprocessor());
	clang::Token tok;

	do {
		ci.getPreprocessor().Lex(tok);
		if( ci.getDiagnostics().hasErrorOccurred())
			break;
		ci.getPreprocessor().DumpToken(tok);
		std::cerr << std::endl;
	} while ( tok.isNot(clang::tok::eof));

	ci.getDiagnosticClient().EndSourceFile();

	return 0;
}
